import { MDXComponents } from 'mdx/types';
import { GetStaticPaths, GetStaticProps } from 'next';
import dynamic from 'next/dynamic';
import Head from 'next/head';
import { useRouter } from 'next/router';
import Script from 'next/script';
import { ComponentType } from 'react';
import { useIntl } from 'react-intl';
import Link from '../../components/atoms/links/link';
import SocialLink, {
type SocialWebsite,
} from '../../components/atoms/links/social-link';
import Spinner from '../../components/atoms/loaders/spinner';
import ResponsiveImage, {
ResponsiveImageProps,
} from '../../components/molecules/images/responsive-image';
import Code from '../../components/molecules/layout/code';
import Gallery from '../../components/organisms/images/gallery';
import Overview, {
type OverviewMeta,
} from '../../components/organisms/layout/overview';
import Sharing from '../../components/organisms/widgets/sharing';
import { getLayout } from '../../components/templates/layout/layout';
import PageLayout, {
type PageLayoutProps,
} from '../../components/templates/page/page-layout';
import styles from '../../styles/pages/project.module.scss';
import {
type NextPageWithLayout,
type ProjectPreview,
type Repos,
} from '../../types/app';
import { loadTranslation, type Messages } from '../../utils/helpers/i18n';
import {
getProjectData,
getProjectFilenames,
} from '../../utils/helpers/projects';
import {
getSchemaJson,
getSinglePageSchema,
getWebPageSchema,
} from '../../utils/helpers/schema-org';
import { capitalize } from '../../utils/helpers/strings';
import useBreadcrumb from '../../utils/hooks/use-breadcrumb';
import useGithubApi, { type RepoData } from '../../utils/hooks/use-github-api';
import useSettings from '../../utils/hooks/use-settings';
const BorderedImage = (props: ResponsiveImageProps) => {
return ;
};
type ProjectPageProps = {
project: ProjectPreview;
translation: Messages;
};
/**
* Project page.
*/
const ProjectPage: NextPageWithLayout = ({ project }) => {
const { id, intro, meta, title } = project;
const { cover, dates, license, repos, seo, technologies } = meta;
const intl = useIntl();
const { items: breadcrumbItems, schema: breadcrumbSchema } = useBreadcrumb({
title,
url: `/projets/${id}`,
});
const ProjectContent: ComponentType = dynamic(
() => import(`../../content/projects/${id}.mdx`),
{
loading: () => ,
}
);
const components: MDXComponents = {
Code,
Gallery,
Image: BorderedImage,
Link,
};
const { website } = useSettings();
const { asPath } = useRouter();
const pageUrl = `${website.url}${asPath}`;
const headerMeta: PageLayoutProps['headerMeta'] = {
publication: { date: dates.publication },
update:
dates.update && dates.update !== dates.publication
? { date: dates.update }
: undefined,
};
/**
* Retrieve the repositories links.
*
* @param {Repos} repos - A repositories object.
* @returns {JSX.Element[]} - An array of SocialLink.
*/
const getReposLinks = (repositories: Repos): JSX.Element[] => {
const links = [];
for (const [name, url] of Object.entries(repositories)) {
const socialWebsite = capitalize(name) as SocialWebsite;
const socialUrl = `https://${name}.com/${url}`;
links.push();
}
return links;
};
const { isError, isLoading, data } = useGithubApi(meta.repos!.github!);
const getGithubData = (key: keyof RepoData) => {
if (isError) return 'Error';
if (isLoading || !data) return ;
switch (key) {
case 'created_at':
return data.created_at;
case 'updated_at':
return data.updated_at;
case 'stargazers_count':
const stars = intl.formatMessage(
{
defaultMessage:
'{starsCount, plural, =0 {No stars on Github} one {# star on Github} other {# stars on Github}}',
id: 'Gnf1Si',
description: 'Projets: Github stars count',
},
{ starsCount: data.stargazers_count }
);
return (
<>
⭐
{stars}
>
);
}
};
const overviewData: OverviewMeta = {
creation: data && { date: getGithubData('created_at') as string },
update: data && { date: getGithubData('updated_at') as string },
license,
popularity: data && getGithubData('stargazers_count'),
repositories: repos ? getReposLinks(repos) : undefined,
technologies,
};
const webpageSchema = getWebPageSchema({
description: seo.description,
locale: website.locales.default,
slug: asPath,
title: seo.title,
updateDate: dates.update,
});
const articleSchema = getSinglePageSchema({
cover: `/projects/${id}.jpg`,
dates,
description: intro,
id: 'project',
kind: 'page',
locale: website.locales.default,
slug: asPath,
title,
});
const schemaJsonLd = getSchemaJson([webpageSchema, articleSchema]);
return (
<>
{`${seo.title} - ${website.name}`}
,
]}
>
>
);
};
ProjectPage.getLayout = (page) =>
getLayout(page, { useGrid: true, withExtraPadding: true });
export const getStaticProps: GetStaticProps = async ({
locale,
params,
}) => {
const translation = await loadTranslation(locale);
const { slug } = params!;
const project = await getProjectData(slug as string);
return {
props: {
project,
translation,
},
};
};
export const getStaticPaths: GetStaticPaths = async () => {
const filenames = getProjectFilenames();
const paths = filenames.map((filename) => {
return {
params: {
slug: filename,
},
};
});
return {
paths,
fallback: false,
};
};
export default ProjectPage;